<?php class Socket {
	protected $Socket;
	protected $Domain;
	protected $Proto;
	protected $Address;
	protected $Port;

	public function __construct ($Address) {
		// parse the address
		if ('/' === substr ($Address, 0, 1)) { // UNIX socket filename
			list ($this->Domain, $this->Proto, $this->Address, $this->Port) = array (AF_UNIX, 0, $Address, NULL);
		} elseif (1 === ($Colons = substr_count ($Address, ':'))) { // an ipv4 address:port
			$AddressParts = explode (':', $Address);
			list ($this->Domain, $this->Proto, $this->Address, $this->Port) = array (AF_INET, SOL_TCP, $AddressParts [0], $AddressParts [1]);
		} elseif ($Colons > 1) { // an ipv6 address:port
			$AddressParts = explode (':', $Address);
			$Port = array_pop ($AddressParts);
			list ($this->Domain, $this->Proto, $this->Address, $this->Port) = array (AF_INET6, SOL_TCP, implode (':', $AddressParts), $Port);
		} else {
			throw new X_Socket ('Invalid socket specification');
		}
		// create the socket and set its options
		$this->Socket = socket_create ($this->Domain, SOCK_STREAM, $this->Proto);
			if (FALSE === $this->Socket) throw new X_Socket ();
		$R = @socket_set_nonblock ($this->Socket);
			if (! $R) throw new X_Socket ($this->Socket);
		$R = @socket_set_option ($this->Socket, SOL_SOCKET, SO_REUSEADDR, 1);
			if (! $R) throw new X_Socket ($this->Socket);
		// bind and listen
		$R = @socket_bind ($this->Socket, $this->Address, $this->Port);
			if (! $R) throw new X_Socket ($this->Socket);
		$R = @socket_listen ($this->Socket);
			if (! $R) throw new X_Socket ($this->Socket);
		// fill in the values once more
		socket_getsockname ($this->Socket, $this->Address, $this->Port);
		Daemon::Debug (M__SOCK_CREATED, $this->Address, (int) $this->Port);
	}
	public function Accept () {
		while ($Connection = @socket_accept ($this->Socket)) {
			Connections::Push ($Connection);
		}
	}
	public function Close () {
		Daemon::Debug (M__SOCK_CLOSING, $this->Address, (int) $this->Port);
		@socket_shutdown ($this->Socket);
		@socket_close ($this->Socket);
		if (AF_UNIX === $this->Domain) {
			@unlink ($this->Address);
		}
	}
}
class X_Socket extends Exception {
	public function __construct ($Socket = NULL) {
		if (is_resource ($Socket)) {
			$Code = socket_last_error ($Socket);
			$Message = socket_strerror ($Code);
		} elseif (is_scalar ($Socket)) {
			$Code = '-1';
			$Message = $Socket;
		} else {
			$Code = socket_last_error ();
			$Message = socket_strerror ($Code);
		}
		parent::__construct ($Message, $Code);
	}
}
?>